Param([switch]$Verbose,[switch]$Confirm)
Add-PSSnapin Microsoft.SharePoint.PowerShell



$location = Get-Location
$logFolder = $location.ProviderPath + "\Logs";
$now = (Date);
$global:errorCount = 0;

$global:verbose = $Verbose;
#File paths
$global:backupFilePath = $location.ProviderPath + "\Permissions.xml"
$global:logFilePath = $logFolder + "\" + $now.ToString("yyyy-MM-dd hh-mm-ss tt") + "-restore.txt";

#Backup Configuration
$global:includeFilePermission = $true;

#check and create Log Folder
$logFolderExist = Test-Path $logFolder;
if($logFolderExist-eq $false)
{
	New-Item -ItemType directory -Path $logFolder | Out-Null
}


####################
#                  #
####################
function Log
{
	Param($msg)
	$now = Date
	$output = "Info:`t" + $now.ToString("yyyy-MM-dd hh:mm:ss tt") + ": ";
	for($i = 0; $i -lt $indent; $i++)
	{
		$output += "`t"
	}
	$output += $msg;
	$output | Out-File $global:logFilePath -Append -Force
	Write-Host $output;
}

####################
#                  #
####################
function Error
{
	Param($msg)
	$now = Date
	$output = "Error:`t" + $now.ToString("yyyy-MM-dd hh:mm:ss tt") + ": " + $msg;
	$output | Out-File $global:logFilePath -Append -Force
	Write-Host -f Red $output;
	$global:errorCount += 1;
}

#############################################################################
# Clearing Permission while keeping Limited Access user Important           #
#############################################################################
function ClearPermission
{
	Param([Microsoft.SharePoint.SPSecurableObject]$obj)
	
	$roleAssignments = $obj.RoleAssignments;
	$count = $roleAssignments.Count;
	for($i = 0; $i -lt $count ; $i++)
	{
		$roleAssignment = $roleAssignments[$i];
		$bindingCount = $roleAssignment.RoleDefinitionBindings.Count
		$clearCounter = 0;
		for($j = 0; $j -lt $bindingCount ; $j++)
		{
			$roleBinding = $roleAssignment.RoleDefinitionBindings[$clearCounter];
			if($roleBinding.Name -ne "Limited Access")
			{
				$roleAssignment.RoleDefinitionBindings.Remove($clearCounter);
			}
			else
			{
				$clearCounter++;
			}
		}
	}
	$obj.Update();
}

####################
#                  #
####################
function UpdatePermission
{
	Param(
		[Microsoft.SharePoint.SPWeb]$spWeb,
		$obj, 
		$itemXml,
		$indent = 1
	)
	$roleAssignments = $itemXml.RoleAssignments.RoleAssignment
	
	if($roleAssignments)
	{
		$isXmlElement = $roleAssignments.GetType().Name -eq "XmlElement";
		if($isXmlElement)
		{
			Log "Processing 1 role assignments..." $indent
		}
		else
		{
			Log "Processing $($roleAssignments.Length) role assignments..." $indent
		}
		
		#check if this is a root web. Root Web does not allow resetting permission.
		if($obj -is [Microsoft.SharePoint.SPWeb] -and $obj.IsRootWeb)
		{
			$count = $obj.RoleAssignments.Count;
			if($global:verbose)
			{
				Log "Clearing existing permission (RootWeb)..." $indent
			}
			ClearPermission $obj
		}
		else
		{
		
			if($obj.HasUniqueRoleAssignments)
			{
				if($global:verbose)
				{
					Log "Clearing existing permission..." $indent
				}
				ClearPermission $obj
			}
			else
			{
				#All Object for restoration needs to be broken permission for update...
				if($global:verbose)
				{
					Log "Breaking Inheritance for unique permission..." $indent
				}
				$obj.BreakRoleInheritance($false);
			}
		}
		
		$verboseMsg = "`t`n=== Restoring Permission ===`n";
		
		$roleAssignments | ForEach-Object{
			$roleAssignment = $null;
			if($_.Group)
			{
				$groupName = $_.Group;
				$spGroup = $spWeb.SiteGroups[$groupName];
                if($spGroup)
				{	
					
					$verboseMsg += "$groupName"
					$roleAssignment = New-Object Microsoft.SharePoint.SPRoleAssignment($spGroup)
				}
				else
				{
					Error "`tCannot find group $groupName";
				}				
				
			}
			
			if($_.User)
			{
				$login = $_.User;
				$spUser = $spWeb.EnsureUser($login);
				if($spUser)
				{
					$verboseMsg += "$login";
					$roleAssignment = New-Object Microsoft.SharePoint.SPRoleAssignment($spUser)
				}
				else
				{
					Error "`tCannot find User $login";
				}
			}
			
			
			$_.RoleDefinitionBindings.RoleDefinition | ForEach-Object {
				$typeName = $_.Name;
				$roleDefinition = $spWeb.RoleDefinitions[$typeName];
                if($roleDefinition)
                {
				    $verboseMsg += "`t - $typeName; `n"
				    $roleAssignment.RoleDefinitionBindings.Add($roleDefinition);
                }
                else
                {
                    Error "`tUnable to find Role Definition '$typeName'";
                }
			}
			
			$obj.RoleAssignments.Add($roleAssignment)
		}
		
		if($global:verbose)
		{
			Log $verboseMsg $indent
		}
		
		$obj.Update();
	}
	else
	{
		if($global:verbose)
		{
			Log "Inheriting Parent Permission." $indent
		}
	}
}



####################
#                  #
####################
function RestoreWebPermission
{
	Param(		
		$webXml
	)
	
	###############################################################
	# Restore web permission
	$webUrl = $webXml.Url;
	$spWeb = Get-SPWeb $webUrl;
	
	if($spWeb)
	{
		Log ("Restoring web " + $spWeb.Url);
		Write-Host $webXml
		UpdatePermission $spWeb $spWeb $webXml
		################################################################
		# Restore Document Library Permission 
		$docLibraries = $webXml.Lists.List
		if($docLibraries.Count -gt 0)
		{
			Log ("Restoring $($docLibraries.Count) Document Libraries..")
			$docLibraries | ForEach-Object {
				RestoreDocumentLibraryPermission $_ $spWeb;
			}
		}
		
		##################################################################
		#backup subweb permission (if any)
		$subWebs = $webXml.Webs.Web;
		if($subWebs.Count -gt 0)
		{
			Log ("Restoring $($subWebs.Count) Sub Webs..")
			$subWebs | ForEach-Object {
				RestoreWebPermission $_;
			}
		}
		
		$spWeb.Dispose();
	}
	else
	{
		Error "Unable to find Web $webUrl"
	}
}

####################
#                  #
####################
function RestoreDocumentLibraryPermission
{
	Param(
		$listXml,
		[Microsoft.SharePoint.SPWeb]$web
	)
	
	#backup document library permission
	$docLibTitle = $listXml.Title
	Log("`tRestoring Document Library ""$docLibTitle""");
	$docLib = $web.Lists[$docLibTitle];
	if($docLib)
	{
		UpdatePermission $spWeb $docLib $listXml 2
	}
	else
	{
		Error "`t`tUnable to locate document library ""$docLibTitle"" in ""$($web.Url)"". Permission not restored successfully.";
	}
	
	
	#backup folders permission
	$rootFolderXml = $listXml.RootFolder
	if($rootFolderXml.SubFolders)
	{
		$subFolders = $rootFolderXml.SubFolders.Folder
		if($subFolders.Count -gt 0)
		{
			Log ("`t`tRestoring $($subFolders.Count) Sub Folders..")
			$rootFolder = $docLib.RootFolder;
			$subFolders | ForEach-Object {
				RestoreFolderPermission $_ $spWeb $rootFolder
			}
		}
		
		#restoring files permission (if configured to do so)
		if($global:includeFilePermission)
		{
			if($rootFolderXml.Files)
			{
				RestoreFilesPermission $rootFolderXml.Files.File $spWeb $rootFolder 2
			}
		}
	}
	else
	{
		if($global:verbose)
		{
			Log ("`t`tLibrary does not contain any folder")
		}
	}
}


####################
#                  #
####################
function RestoreFolderPermission
{
	Param(
		$folderXml,
		[Microsoft.SharePoint.SPWeb]$web,
		[Microsoft.SharePoint.SPFolder]$parentFolder,
		$indent = 3
	)
	if($parentFolder)
	{
		$folder = $parentFolder.SubFolders[$folderXml.Name]
		
		#restoring current folder permission	
		if($folder -and $folder.Item)
		{
			Log "Processing SPFolder ""$($folder.Name)""" $indent
			if($folderXml.RoleAssignments)
			{	
				$folderItem = $folder.Item;
				UpdatePermission $spWeb $folderItem $folderXml $indent
			}
			else
			{
				if($global:verbose)
				{
					Log "No unique permission..." $indent
				}
			}
			
			#restoring subfolder permission (if any)
			if($folderXml.SubFolders)
			{
				$subFolders = $folderXml.SubFolders.Folder
				if($subFolders.Count -gt 0)
				{
					Log "Restoring $($subFolders.Count) Sub Folders.." $indent
					$subFolders | ForEach-Object {
						RestoreFolderPermission $_ $spWeb $folder ($indent+1)
					}
				}
			}
			
			#restoring files permission (if configured to do so)
			if($global:includeFilePermission)
			{
				if($folderXml.Files)
				{
					RestoreFilesPermission $folderXml.Files.File $spWeb $folder $indent
				}
			}
		}
		else
		{
			Error "`t`t`tUnable to find SPFolder ""$($folderXml.Name)"" in ""$($parentFolder.ServerRelativeUrl)"". Permission not restored successfully."
		}
	}
	else
	{
		Error "`t`t`tNo Parent Folder provided..."
	}
}



####################
#                  #
####################
function RestoreFilesPermission
{
	Param(
		$filesXML,
		[Microsoft.SharePoint.SPWeb]$web,
		[Microsoft.SharePoint.SPFolder]$parentFolder,
		$indent = 3
	)
	
	if($filesXML)
	{
		#backup files permission
		
		$isXmlElement = $filesXML.GetType().Name -eq "XmlElement";
		if($isXmlElement)
		{
			Log "Processing 1 file..." $indent
		}
		else
		{
			Log "Processing $($filesXML.Length) files..." $indent
		}	
		
		$filesXML | ForEach-Object {
			$file = $parentFolder.Files[$_.Name];
			if($file -and $file.Item)
			{
				UpdatePermission $spWeb $file.Item $_ $indent
			}
			else
			{
				Error "`t`t`tUnable to find SPFile ""$($_.Name)"" in ""$($parentFolder.ServerRelativeUrl)"". Permission not restored successfully."
			}
		}
	}
	else
	{
		if($global:verbose)
		{
			Log "No files backup was found" $indent
		}
	}
}

Write-Host -f Red "Warning! You will be restoring the following SPSite...";
$xml = [xml](Get-Content $global:backupFilePath -Encoding UTF8);
$xml.SharePoint.Sites.Site | ForEach-Object { Write-Host -f Red "`t$($_.Url)" }
if($Confirm)
{
	#Do nothing...
}
else
{
	$key = Read-Host "Enter 'Y' to proceed";
	if($key -ne "Y" -and $key -ne "y")
	{
		Write-Host "Phew..."
		Write-Host
		return;
	}
}

Log "=== Starting Scripting ===";
Try
{
	
	
	$xml.SharePoint.Sites.Site | ForEach-Object {
		$siteXml = $_;
		$siteUrl = $siteXml.Url;
		Log "Restoring site $siteUrl"
		
		$rootWebXml = $siteXml.RootWeb;
		if($rootWebXml.RoleAssignments)
		{
			RestoreWebPermission $rootWebXml
		}
		else
		{
			Error "`tRootWeb must have RoleAssignements.. Please check your backup file"
		}
	}
	
	if($global:errorCount -gt 0)
	{
		Log "Script executed with $($global:errorCount) Error(s).";
		Write-Host -f Red "Please view log file at $($global:logFilePath)";
	}
	
}
catch [Exception]
{
	Error ("Error restoring permission... `n" + $_.Exception)
}

Log "=== Script Completed ==="

